package com.ideaaedi.springcloud.jd.user.support;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ideaaedi.springcloud.jd.commonds.entity.BaseCodeMsgEnum;
import com.ideaaedi.springcloud.jd.commonds.enums.user.CertTypeEnum;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysGroupPO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysMenuPO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysPostPO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysRolePO;
import com.ideaaedi.springcloud.jd.commonds.entity.user.po.SysUserPO;
import com.ideaaedi.springcloud.jd.commonds.enums.user.UserTypeEnum;
import com.ideaaedi.springcloud.jd.commonds.exception.BaseException;
import com.ideaaedi.springcloud.jd.commonspring.mybatisplus.tenant.TenantProvider;
import com.ideaaedi.springcloud.jd.commonspring.validator.SysValidator;
import com.ideaaedi.springcloud.jd.user.service.SysGroupService;
import com.ideaaedi.springcloud.jd.user.service.SysMenuService;
import com.ideaaedi.springcloud.jd.user.service.SysPostService;
import com.ideaaedi.springcloud.jd.user.service.SysRoleService;
import com.ideaaedi.springcloud.jd.user.service.SysUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * 用户服务相关check支持
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.5.B
 */
@Component
public class UserCheckSupport implements SysValidator<Void> {
    
    @Resource
    private SysGroupService sysGroupService;
    
    @Resource
    private SysMenuService sysMenuService;
    
    @Resource
    private SysPostService SysPostService;
    
    @Resource
    private SysRoleService sysRoleService;
    
    @Resource
    private SysUserService sysUserService;
    
    /**
     * 校验相同租户中是否存在相同的角色code
     *
     * @param tenant 租户
     * @param code 要校验的code
     * @param excludedId 要排除的code
     *
     * @throws BaseException 当存在时抛出{@link BaseCodeMsgEnum#CODE_BEEN_OCCUPIED}
     */
    public void checkExistSameRoleCode(@NonNull String tenant, @NonNull String code, @Nullable Long excludedId) {
        Objects.requireNonNull(tenant, "tenant cannot be null.");
        Objects.requireNonNull(code, "code cannot be null.");
        LambdaQueryWrapper<SysRolePO> limitOneQuery = new LambdaQueryWrapper<SysRolePO>()
                .eq(SysRolePO::getCode, code)
                .eq(SysRolePO::getTenant, tenant)
                .ne(excludedId != null, SysRolePO::getId, excludedId)
                .last("limit 1");
        SysRolePO sameCodeRole = TenantProvider.execGlobal(() -> sysRoleService.getOne(limitOneQuery));
        if (sameCodeRole != null) {
            throw new BaseException("Exist save code data -> " + JSON.toJSONString(sameCodeRole),
                    BaseCodeMsgEnum.CODE_BEEN_OCCUPIED, code);
        }
    }
    
    /**
     * 校验相同租户相同用户类型中是否存在相同的手机号
     *
     * @param tenant 租户
     * @param type 用户类型
     * @param phone 要校验的手机号
     * @param excludedId 要排除的code
     *
     * @throws BaseException 当存在时抛出{@link BaseCodeMsgEnum#PHONE_IS_OCCUPIED}
     */
    public void checkExistSamePhone(@NonNull String tenant, @NonNull UserTypeEnum type, @NonNull String phone, @Nullable Long excludedId) {
        Objects.requireNonNull(tenant, "tenant cannot be null.");
        Objects.requireNonNull(type, "type cannot be null.");
        Objects.requireNonNull(phone, "phone cannot be null.");
        LambdaQueryWrapper<SysUserPO> queryWrapper = new LambdaQueryWrapper<SysUserPO>()
                .select(SysUserPO::getId)
                .eq(SysUserPO::getPhone, phone)
                .eq(SysUserPO::getTenant, tenant)
                .eq(SysUserPO::getType, type)
                .ne(excludedId != null, SysUserPO::getId, excludedId)
                .last("limit 1");
        boolean phoneUsed = TenantProvider.execGlobal(() -> sysUserService.count(queryWrapper) > 0);
        if (phoneUsed) {
            throw new BaseException(BaseCodeMsgEnum.PHONE_IS_OCCUPIED);
        }
    }
    
    /**
     * 校验相同租户相同用户类型中是否存在相同的 证件号
     *
     * @param tenant 租户
     * @param type 用户类型
     * @param certType 要校验的证件类型
     * @param certNo 要校验的证件号
     * @param excludedId 要排除的code
     *
     * @throws BaseException 当存在时抛出{@link BaseCodeMsgEnum#CERT_IS_OCCUPIED}
     */
    public void checkExistSameCert(@NonNull String tenant, @NonNull UserTypeEnum type,
                                   @NonNull CertTypeEnum certType, @NonNull String certNo,
                                   @Nullable Long excludedId) {
        Objects.requireNonNull(tenant, "tenant cannot be null.");
        Objects.requireNonNull(type, "type cannot be null.");
        Objects.requireNonNull(certType, "certType cannot be null.");
        Objects.requireNonNull(certNo, "certNo cannot be null.");
        LambdaQueryWrapper<SysUserPO> queryWrapper = new LambdaQueryWrapper<SysUserPO>()
                .select(SysUserPO::getId)
                .eq(SysUserPO::getTenant, tenant)
                .eq(SysUserPO::getType, type)
                .eq(SysUserPO::getCertType, certType)
                .eq(SysUserPO::getCertNo, certNo)
                .ne(excludedId != null, SysUserPO::getId, excludedId)
                .last("limit 1");
        boolean certUsed = TenantProvider.execGlobal(() -> sysUserService.count(queryWrapper) > 0);
        if (certUsed) {
            throw new BaseException(BaseCodeMsgEnum.CERT_IS_OCCUPIED);
        }
    }
    
    /**
     * 校验相同租户中是否存在相同的菜单code
     *
     * @param tenant 租户
     * @param code 要校验的code
     * @param excludedId 要排除的code
     *
     * @throws BaseException 当存在时抛出{@link BaseCodeMsgEnum#CODE_BEEN_OCCUPIED}
     */
    public void checkExistSameMenuCode(@NonNull String tenant, @NonNull String code, @Nullable Long excludedId) {
        Objects.requireNonNull(tenant, "tenant cannot be null.");
        Objects.requireNonNull(code, "code cannot be null.");
        SysMenuPO sameCodePo = sysMenuService.getOne(new LambdaQueryWrapper<SysMenuPO>()
                .eq(SysMenuPO::getCode, code)
                .eq(SysMenuPO::getTenant, tenant)
                .ne(excludedId != null, SysMenuPO::getId, excludedId)
                .last("limit 1")
        );
        if (sameCodePo != null) {
            throw new BaseException("Exist same menu code " + JSON.toJSONString(sameCodePo),
                    BaseCodeMsgEnum.CODE_BEEN_OCCUPIED, code);
        }
    }
    
    /**
     * 校验相同租户中是否存在相同的职位code
     *
     * @param tenant 租户
     * @param code 要校验的code
     * @param excludedId 要排除的code
     *
     * @throws BaseException 当存在时抛出{@link BaseCodeMsgEnum#CODE_BEEN_OCCUPIED}
     */
    public void checkExistSamePostCode(@NonNull String tenant, @NonNull String code, @Nullable Long excludedId) {
        Objects.requireNonNull(tenant, "tenant cannot be null.");
        Objects.requireNonNull(code, "code cannot be null.");
        SysPostPO sameCodePo = SysPostService.getOne(new LambdaQueryWrapper<SysPostPO>()
                .eq(SysPostPO::getCode, code)
                .eq(SysPostPO::getTenant, tenant)
                .ne(excludedId != null, SysPostPO::getId, excludedId)
                .last("limit 1")
        );
        if (sameCodePo != null) {
            throw new BaseException("Exist same post code " + JSON.toJSONString(sameCodePo),
                    BaseCodeMsgEnum.CODE_BEEN_OCCUPIED, code);
        }
    }
    
    /**
     * 校验相同租户中是否存在相同的组code
     *
     * @param tenant 租户
     * @param code 要校验的code
     * @param excludedId 要排除的code
     *
     * @throws BaseException 当存在时抛出{@link BaseCodeMsgEnum#CODE_BEEN_OCCUPIED}
     */
    public void checkExistSameGroupCode(@NonNull String tenant, @NonNull String code, @Nullable Long excludedId) {
        Objects.requireNonNull(tenant, "tenant cannot be null.");
        Objects.requireNonNull(code, "code cannot be null.");
        SysGroupPO sameCodePo = sysGroupService.getOne(new LambdaQueryWrapper<SysGroupPO>()
                .eq(SysGroupPO::getCode, code)
                .eq(SysGroupPO::getTenant, tenant)
                .ne(excludedId != null, SysGroupPO::getId, excludedId)
                .last("limit 1")
        );
        if (sameCodePo != null) {
            throw new BaseException("Exist same group code " + JSON.toJSONString(sameCodePo),
                    BaseCodeMsgEnum.CODE_BEEN_OCCUPIED, code);
    
        }
    }
    
    /**
     * 校验账号特殊字符
     *
     * @param accountNo 账号
     *
     * @throws BaseException 当含有特殊字符时抛出{@link BaseCodeMsgEnum#ACCOUNT_NO_CANNOT_CONTAIN_SPECIAL_CHARACTERS}
     */
    public void checkAccountNoSpecialCharacter(@NonNull String accountNo) {
        Assert.isTrue(StringUtils.isNotBlank(accountNo), "accountNo cannot be blank.");
        // 特殊字符检验
        if (accountNo.contains("{")) {
            throw new BaseException(BaseCodeMsgEnum.ACCOUNT_NO_CANNOT_CONTAIN_SPECIAL_CHARACTERS);
        }
    }
}